-- Matías Blanco & Sergio Pérez
-- Aeropuerto. Práctica 2 - Tipos Protegidos en ADA

with Ada.Text_Io ; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Float_Random ; use Ada.Numerics.Float_Random;

procedure AeroCitasAda is
   -- Costantes y variables
   MAXAVIONES : constant integer := 50;
   -- Definimos el tipo enumerador
   type EstadoPista is (Aterrizar, Despegar, Vacia);

   -- Definimos el tipo registro Pista
   type Pista is
   record
      estado : EstadoPista := Vacia;
      naviones : integer := 0; -- Numero de aviones en la pista
   end record;

   -- Definicion de tipos
   type TPistas is array(1..2) of Pista;

   -- Definimos las tareas
   protected type TorreControl is
      procedure QuiereAterrizar;
      entry Aterrizar(pista : out integer);
      entry Despegar(pista : out integer);
      procedure DejarPista(pista : in integer);
      private
         Pistas : TPistas;
         esperandoAterrizar : integer := 0;
   end TorreControl;

   task type Avion(idAvion : integer);

   -- Definimos a TorreControl para que controle a las aviones
   control : TorreControl;

   type PAvion is access Avion; -- Definitos el tipo puntero de Avion
   -- Definimos el array de punteros de aviones
   aviones: array(1..MAXAVIONES) of PAvion;

   protected body TorreControl is
      procedure QuiereAterrizar is
      begin
         esperandoAterrizar := esperandoAterrizar + 1;
      end QuiereAterrizar;      

      entry Aterrizar(pista : out integer) when Pistas(1).estado = Vacia or Pistas(2).estado = Vacia is
      begin
         -- Vemos la pista que esta vacia
         if Pistas(1).estado = Vacia then
            pista := 1;
         elsif Pistas(2).estado = Vacia then
            pista := 2;
         end if;
         Pistas(pista).estado := Aterrizar;
         Pistas(pista).naviones := Pistas(pista).naviones + 1;
         esperandoAterrizar := esperandoAterrizar - 1;
      end Aterrizar;
      
      entry Despegar(pista : out integer) when ((Pistas(1).estado = Vacia or (Pistas(1).estado = Despegar and Pistas(1).naviones < 3)) or (Pistas(2).estado = Vacia or (Pistas(2).estado = Despegar and Pistas(2).naviones < 3))) and esperandoAterrizar = 0 is
      begin
         -- Vemos la pista que esta vacia
         if Pistas(1).estado = Vacia or (Pistas(1).estado = Despegar and Pistas(1).naviones < 3) then
            pista := 1;
         elsif Pistas(2).estado = Vacia or (Pistas(2).estado = Despegar and Pistas(2).naviones < 3) then
            pista := 2;
         end if;
         Pistas(pista).estado := Despegar;
         Pistas(pista).naviones := Pistas(pista).naviones + 1;
      end Despegar;
      
      procedure DejarPista(pista : in integer) is
      begin
         Pistas(pista).naviones := Pistas(pista).naviones - 1;
         if Pistas(pista).naviones = 0 then
            Pistas(pista).estado := Vacia;
         end if;
      end DejarPista;
   end TorreControl;

   task body Avion is
      id: integer;
      pista: integer;
   begin
      -- Inicializamos los valores
      id := idAvion;
      loop
         -- Despegar
         Put("-- El avion ");Put(id,0);Put(" quiere despegar.");New_Line;
         control.Despegar(pista);
         Put("++> El avion ");Put(id,0);Put(" esta despegando en la pista ");Put(pista,0);New_Line;
         delay 1.0; -- Tiempo que tarda en despegar
         Put("--> El avion ");Put(id,0);Put(" ha despegado en la pista ");Put(pista,0);New_Line;
         control.DejarPista(pista);

         delay 3.0; -- Tiempo que esta volando

         -- Aterrizar
         Put("-- El avion ");Put(id,0);Put(" quiere aterrizar.");New_Line;
         control.QuiereAterrizar;
         control.Aterrizar(pista);
         Put("<++ El avion ");Put(id,0);Put(" esta aterrizando en la pista ");Put(pista,0);New_Line;
         delay 1.0; -- Tiempo que tarda en aterrizar
         Put("<-- El avion ");Put(id,0);Put(" ha aterrizado en la pista ");Put(pista,0);New_Line;
         control.DejarPista(pista);

         delay 5.0; -- Tiempo de descanso
      end loop;
   end Avion;
begin
   Put("---- COMIENZAN A SALIR AVIONES");New_Line;New_Line;
   for i in 1..MAXAVIONES loop
      aviones(i) := new Avion(i);
   end loop;
end AeroCitasAda;
